package com.my.core.util.code;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
//redis注释
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.core.ValueOperations;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

/**
 * 订单码生成工具类  使用redis的话 搜索<redis注释> 然后取消对应注释
 * 支持本地缓存与Redis缓存切换，通过注释控制缓存实现
 * 分布式部署最佳实践：
 *  环境变量配置：
 *      # 为每个实例分配唯一标识
 *      export WORKER_ID=1       # 机器ID（0-31）
 *      export DATA_CENTER_ID=2  # 数据中心ID（0-31）
 *  数据库唯一性约束：
 *      ALTER TABLE orders ADD UNIQUE KEY uk_order_code (order_code);
 */
public class OrderCodeGenerator {
    // 默认前缀
    private static final String DEFAULT_PREFIX = "ORD";
    // 数据中心 ID（可通过环境变量配置）
    private static final long DATA_CENTER_ID = getDatacenterId();
    // 机器 ID（自动生成）
    private static final long WORKER_ID = getWorkerId();
    // Snowflake 实例
    private static final Snowflake SNOWFLAKE = IdUtil.createSnowflake(WORKER_ID, DATA_CENTER_ID);
    // 短码字符集（去除易混淆字符）
    private static final String CODE_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
    // 日期格式化器（年月）
    private static final SimpleDateFormat MONTH_FORMAT = new SimpleDateFormat("yyMM");
    // 日期格式化器（年月日）
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
    // 最大序列值
    private static final int MAX_SEQUENCE = 9999;
    // 自增序列（时间戳模式）
    private static final AtomicInteger TIME_SEQUENCE = new AtomicInteger(0);
    // 锁（时间戳模式同步）
    private static final Lock TIME_LOCK = new ReentrantLock();
    // 上次时间戳（时间戳模式）
    private static String LAST_TIMESTAMP = "";
    // 缓存前缀
    private static final String CACHE_PREFIX = "order:stats:";
    // 缓存过期时间（天）
    private static final int CACHE_DAYS = 7;

    // 统计指标
    private static final AtomicInteger TOTAL_GENERATIONS = new AtomicInteger(0);
    private static final AtomicInteger ERROR_COUNT = new AtomicInteger(0);

    // redis注释 Redis模板（需通过Spring注入）
//    private static RedisTemplate<String, String> redisTemplate;
    // 在配置类中注入RedisTemplate
    //也提供了直接传入的方法
    /* @Configuration
    public class OrderCodeConfig {
        @Autowired
        private RedisTemplate<String, String> redisTemplate;

        @PostConstruct
        public void init() {
            OrderCodeGenerator.setRedisTemplate(redisTemplate);
        }
    }*/
    /**
     * 设置RedisTemplate（供Spring注入）
     * redis注释
     */
//    public static void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
//        OrderCodeGenerator.redisTemplate = redisTemplate;
//    }

    /**
     * 生成带月份前缀的唯一订单码（完全唯一）
     */
    public static String generateWithMonthPrefix() {
        return generateOrderCode(MONTH_FORMAT.format(new Date()));
    }

    /**
     * 生成带自定义前缀的唯一订单码（完全唯一）
     */
    public static String generateOrderCode(String prefix) {
        return generateOrderCode(prefix, false);
    }

    /**
     * 生成订单码（支持长码/短码模式）
     * @param prefix 前缀（可为空，使用默认前缀）
     * @param useShortCode 是否使用短码模式（注意短码有极小概率冲突）
     */
    public static String generateOrderCode(String prefix, boolean useShortCode) {
        TOTAL_GENERATIONS.incrementAndGet();
        String finalPrefix = (prefix == null || prefix.isEmpty()) ? DEFAULT_PREFIX : prefix;

        try {
            if (useShortCode) {
                return generateShortCodeOrder(finalPrefix);
            } else {
                return generateLongCodeOrder(finalPrefix);
            }
        } catch (Exception e) {
            ERROR_COUNT.incrementAndGet();
            // 错误降级：使用时间戳+随机数
            return finalPrefix + System.currentTimeMillis() + RandomUtil.randomNumbers(4);
        }
    }

    /**
     * 生成完全唯一的长码订单（基于Snowflake ID）
     */
    private static String generateLongCodeOrder(String prefix) {
        return prefix + SNOWFLAKE.nextId();
    }

    /**
     * 生成短码订单（带冲突重试机制）
     */
    private static String generateShortCodeOrder(String prefix) {
        final int MAX_RETRIES = 5;
        ConcurrentHashMap<String, Boolean> localCache = new ConcurrentHashMap<>(10000);

        for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
            long id = SNOWFLAKE.nextId();
            String shortCode = convertToBase32(id, 8); // 8位短码
            String orderCode = prefix + shortCode;

            // 双重验证唯一性（本地缓存+分布式缓存/本地缓存）
            if (localCache.putIfAbsent(orderCode, true) == null &&
                    validateUniqueness(orderCode)) {
                return orderCode;
            }

            // 指数退避策略：重试间隔随次数呈指数增长
            if (attempt > 0) {
                try {
                    Thread.sleep((1 << attempt) * 10); // 2^attempt * 10ms
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }

        // 重试失败降级为长码模式
        return generateLongCodeOrder(prefix);
    }

    /**
     * 将长整型 ID 转换为指定长度的 Base32 编码
     */
    private static String convertToBase32(long id, int length) {
        StringBuilder sb = new StringBuilder();
        long temp = id;
        int charsLength = CODE_CHARS.length();

        while (temp > 0) {
            sb.append(CODE_CHARS.charAt((int) (temp % charsLength)));
            temp /= charsLength;
        }

        // 不足指定长度时补全随机字符，保证固定长度
        while (sb.length() < length) {
            sb.append(RandomUtil.randomChar(CODE_CHARS));
        }

        return sb.toString();
    }

    /**
     * 验证订单码唯一性（可切换Redis缓存或本地缓存）
     * 注：放开Redis相关代码前需先注入redisTemplate
     */
    private static boolean validateUniqueness(String orderCode) {
        // === Redis缓存实现（需放开注释使用）===
        //redis注释
//        if (redisTemplate != null) {
//            String cacheKey = "order:code:" + orderCode;
//            ValueOperations<String, String> ops = redisTemplate.opsForValue();
//            return ops.setIfAbsent(cacheKey, "1", 5, TimeUnit.MINUTES);
//        }

        // === 本地缓存实现（默认使用）===
        return LOCAL_UNIQUE_CACHE.putIfAbsent(orderCode, true) == null;
    }

    /**
     * 本地唯一性缓存（默认实现）
     */
    private static final ConcurrentHashMap<String, Boolean> LOCAL_UNIQUE_CACHE = new ConcurrentHashMap<>(10000);

    /**
     * 生成基于时间戳+自增序列的订单号
     * 格式: yyyyMMddHHmmss+4位自增序列
     */
    public static String generateTimestampOrderNo() {
        TIME_LOCK.lock();
        try {
            String currentDate = LocalDateTime.now().format(DATE_FORMAT);
            String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss"));
            String currentTimestamp = currentDate + currentTime;

            // 日期变化或序列达到最大值时重置计数器
            if (!currentDate.equals(LAST_TIMESTAMP) || TIME_SEQUENCE.get() >= MAX_SEQUENCE) {
                TIME_SEQUENCE.set(0);
                LAST_TIMESTAMP = currentDate;
            }

            int seq = TIME_SEQUENCE.incrementAndGet();
            return currentTimestamp + String.format("%04d", seq);
        } finally {
            TIME_LOCK.unlock();
        }
    }

    /**
     * 生成带业务前缀的订单号
     * 格式: 前缀+yyyyMMdd+自定义数字（补零到6位）
     */
    public static String generateBusinessOrderNo(String prefix, int num) {
        String date = LocalDateTime.now().format(DATE_FORMAT);
        // 将数字格式化为6位，不足前面补零（如1→000001）
        String formattedNum = String.format("%06d", num);
        return prefix + date + formattedNum;
    }

    /**
     * 基于UUID生成订单号（截取前16位）
     */
    public static String generateUUIDOrderNo() {
        String uuid = UUID.randomUUID().toString().replace("-", "");
        return uuid.substring(0, 16); // 取前16位保证长度一致
    }

    /**
     * 生成包含用户标识的订单号
     * 格式: 前缀+用户ID(4位)+时间戳后6位+2位随机数
     */
    public static String generateUserRelatedOrderNo(String prefix, String userId) {
        String formattedUserId = formatUserId(userId);
        String timePart = LocalDateTime.now().format(DATE_FORMAT).substring(8); // 取日期后6位
        int randomPart = (int) (Math.random() * 90 + 10); // 生成10-99的两位随机数
        return prefix + formattedUserId + timePart + randomPart;
    }

    private static String formatUserId(String userId) {
        if (userId == null || userId.isEmpty()) {
            return "0000"; // 默认返回4位零
        }

        // 过滤非数字字符，保证安全性
        userId = userId.replaceAll("\\D", "");

        if (userId.length() >= 4) {
            return userId.substring(userId.length() - 4); // 取最后4位
        } else {
            return String.format("%04d", Integer.parseInt(userId)); // 不足4位前面补零
        }
    }

    /**
     * 获取数据中心 ID（0-31），优先从环境变量获取
     */
    private static long getDatacenterId() {
        try {
            String envValue = System.getenv("DATA_CENTER_ID");
            if (envValue != null && !envValue.isEmpty()) {
                return Math.max(0, Math.min(31, Long.parseLong(envValue))); // 限制在0-31范围内
            }

            InetAddress address = InetAddress.getLocalHost();
            String hostAddress = address.getHostAddress();
            return Math.abs(hostAddress.hashCode()) % 32; // 取哈希值模32
        } catch (UnknownHostException e) {
            return RandomUtil.randomLong(0, 32); // 异常时返回随机值
        }
    }

    /**
     * 获取机器 ID（0-31），优先从环境变量获取
     */
    private static long getWorkerId() {
        try {
            String envValue = System.getenv("WORKER_ID");
            if (envValue != null && !envValue.isEmpty()) {
                return Math.max(0, Math.min(31, Long.parseLong(envValue))); // 限制范围
            }

            String macAddress = NetUtil.getLocalMacAddress();
            return Math.abs(macAddress.hashCode()) % 32; // 基于MAC地址哈希
        } catch (Exception e) {
            return RandomUtil.randomLong(0, 32); // 异常处理
        }
    }

    /**
     * 统计今日订单数据（可切换Redis缓存或本地缓存）
     * 注：放开Redis相关代码前需先注入redisTemplate
     * @return 今日订单数量
     */
    public static long getTodayOrderCount() {
        String today = LocalDateTime.now().format(DATE_FORMAT);
        String cacheKey = CACHE_PREFIX + today;

        // === Redis缓存实现（需放开注释使用）redis注释===
//        if (redisTemplate != null) {
//            ValueOperations<String, String> ops = redisTemplate.opsForValue();
//            long count = ops.increment(cacheKey, 1);
//            redisTemplate.expire(cacheKey, CACHE_DAYS, TimeUnit.DAYS);
//            return count;
//        }

        // === 本地缓存实现（默认使用）===
        return getLocalDailyCount(today);
    }
    /**
     * 统计今日订单数据（优先使用 Redis，无 Redis 时使用本地缓存）
     * redis注释
     */
//    public static long getTodayOrderCount(RedisTemplate<String, String> redisTemplate) {
//        String today = LocalDateTime.now().format(DATE_FORMAT);
//        String cacheKey = CACHE_PREFIX + today;
//
//        // Redis 实现（需要传入 redisTemplate）
//        if (redisTemplate != null) {
//            ValueOperations<String, String> ops = redisTemplate.opsForValue();
//            long count = ops.increment(cacheKey, 1);
//            redisTemplate.expire(cacheKey, CACHE_DAYS, TimeUnit.DAYS);
//            return count;
//        }
//        // 本地缓存实现（分布式环境下可能计数不准）
//        return getLocalDailyCount(today);
//    }
//    /**
//     * 结合缓存统计生成带有序编号的订单号
//    * redis注释
//     */
//    public static String generateOrderNoWithDailySeq(String prefix, RedisTemplate<String, String> redisTemplate) {
//        String date = LocalDateTime.now().format(DATE_FORMAT);
//        long todayCount = getTodayOrderCount(redisTemplate);
//        String seq = String.format("%06d", todayCount);
//        return prefix + date + seq;
//    }
    /**
     * 本地今日订单计数器（默认实现）
     * 注：分布式环境下需使用Redis实现
     */
    private static final ConcurrentHashMap<String, AtomicLong> LOCAL_DAILY_COUNTER = new ConcurrentHashMap<>();
    private static long getLocalDailyCount(String today) {
        // 使用computeIfAbsent确保原子性创建计数器
        AtomicLong counter = LOCAL_DAILY_COUNTER.computeIfAbsent(today, k -> new AtomicLong(0));
        return counter.incrementAndGet();
    }

    /**
     * 结合缓存统计生成带有序编号的订单号
     * 格式: 前缀+日期+当日序号（基于缓存统计）
     */
    public static String generateOrderNoWithDailySeq(String prefix) {
        String date = LocalDateTime.now().format(DATE_FORMAT);
        long todayCount = getTodayOrderCount(); // 获取今日订单数作为序号

        // 序号格式化为6位，如1→000001
        String seq = String.format("%06d", todayCount);
        return prefix + date + seq;
    }

    /**
     * 获取统计指标（总生成数+错误数）
     */
    public static Stats getStats() {
        return new Stats(
                TOTAL_GENERATIONS.get(),
                ERROR_COUNT.get()
        );
    }

    /**
     * 统计指标类
     */
    public static class Stats {
        private final int totalGenerations;
        private final int errorCount;

        public Stats(int totalGenerations, int errorCount) {
            this.totalGenerations = totalGenerations;
            this.errorCount = errorCount;
        }

        // Getter方法
        public int getTotalGenerations() { return totalGenerations; }
        public int getErrorCount() { return errorCount; }

        @Override
        public String toString() {
            return "Stats{" +
                    "totalGenerations=" + totalGenerations +
                    ", errorCount=" + errorCount +
                    ", errorRate=" + String.format("%.4f%%", (double) errorCount / totalGenerations * 100) +
                    '}';
        }
    }

    public static void main(String[] args) {
        // 示例1：使用默认前缀生成短码（本地缓存）
        System.out.println("默认前缀短码: " + generateOrderCode(null, true));

        // 示例2：使用月份前缀生成长码
        System.out.println("月份前缀长码: " + generateOrderCode(generateWithMonthPrefix(), false));

        // 示例3：自定义前缀业务订单号
        System.out.println("业务订单号: " + generateBusinessOrderNo("SD", 123));

        // 示例4：时间戳+序列订单号
        System.out.println("时间戳订单号: " + generateTimestampOrderNo());

        // 示例5：UUID订单号
        System.out.println("UUID订单号: " + generateUUIDOrderNo());

        // 示例6：用户相关订单号
        System.out.println("用户订单号: " + generateUserRelatedOrderNo("U", "123456"));

        // 示例7：获取今日订单数并生成订单号（本地缓存）
        System.out.println("今日订单数: " + getTodayOrderCount());
        //推荐方式
        System.out.println("带序号订单号: " + generateOrderNoWithDailySeq("ORD"));
//        String orderNo = OrderCodeGenerator.generateOrderNoWithDailySeq("ORD", redisTemplate);

        // 生成10个不同类型订单码
        System.out.println("\n批量生成订单码:");
        for (int i = 0; i < 5; i++) {
            System.out.println("短码模式: " + generateOrderCode("ORD", true));
            System.out.println("长码模式: " + generateOrderCode("ORD", false));
        }

//        @Service
//        public class OrderService {
//            private final RedisTemplate<String, String> redisTemplate;
//
//            @Autowired
//            public OrderService(RedisTemplate<String, String> redisTemplate) {
//                this.redisTemplate = redisTemplate;
//            }
//            public String createOrder() {
//                // 使用传入的 redisTemplate 生成订单号
//                String orderNo = OrderCodeGenerator.generateOrderNoWithDailySeq("ORD", redisTemplate);
//                // ... 其他业务逻辑
//                return orderNo;
//            }
//        }
    }
}